#define _GNU_SOURCE
#include <fcntl.h>              /* Definition of O_* constants */
#include <unistd.h>
#define DBG_MEM
#include "log/log.h"
#include "list/list.h"
#include "memleak/memleak.h"
#include "libuvweb.h"
#include "uv.h"

uv_loop_t uv_loop;
libuvweb_t libuvweb = { .inited = 0 };
struct log_handle *log_handle = NULL;
int log_with_ts = 1;
int log_with_func = 1;
int log_with_line = 1;
int log_with_file = 1;
int verbose = 1;
int log_file_info_width = 30;
int log_func_info_width = 30;
struct memleak_handle memleak_handle;


#define UVWEB_FLAG_HAVE_ADD_RESPONSE_HEAD 0x01
struct uvweb_userdata {
    char last_line[512]; int last_line_len;
    char *filedata; int filedata_size; int filedata_len;
    int status;
    int similar_mode;
    int compare_area;
    int substring_type;
    int timeout;
    time_t start_ts;
    int flag;
};

#define _uvweb_userdata_free(arg) do {\
    struct uvweb_userdata *_userdata = arg; \
    if (_userdata) free(_userdata);\
} while(0)
void uvweb_userdata_free(void *arg) { _uvweb_userdata_free(arg); }

int libuvweb_cb(libuvweb_t *libuvweb, uvweb_session_t *uvweb)
{
    if (uvweb->request_line == NULL) { LOGERR("bug, fix me\n"); return -1; }
    char peer_host[63] = ""; uint16_t peer_port = 0; getPeerInfo((uv_tcp_t *)&uvweb->uv_tcp, peer_host, sizeof(peer_host), &peer_port);
    uint32_t dump_flag = 0;
    if (uvweb->data == NULL) { uvweb->data = calloc(1, sizeof(struct uvweb_userdata)); if (uvweb->data == NULL) { LOGERR("fail to calloc\n"); return -1; } }
    struct uvweb_userdata *data = uvweb->data;
    LOGERR("%-15s request_line: %s\n", peer_host, uvweb->request_line);
    //if (checkout_flag(uvweb->request_line, strlen(uvweb->request_line), &dump_flag)) {
    //    libuvweb_add_response(uvweb, "%s", HTTP_RESPONSE_LINE_404);
    //    libuvweb_add_response(uvweb, "%s", HTTP_HEAD_C0);
    //    libuvweb_add_response(uvweb, "\r\n");
    //    libuvweb_write(uvweb);
    //    return 0;
    //}
    if (dump_flag & DUMP_FLAG_HTTPGZIP && uvweb->z == NULL) {
        uvweb->z = calloc(1, sizeof(z_stream)); if (uvweb->z == NULL) { LOGERR("fail to calloc[%m]\n"); return -1; }
        deflateInit2(uvweb->z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY);
    }


    if ((data->flag & UVWEB_FLAG_HAVE_ADD_RESPONSE_HEAD) == 0) {
        data->flag |=UVWEB_FLAG_HAVE_ADD_RESPONSE_HEAD;
        libuvweb_add_response(uvweb, "%s", HTTP_RESPONSE_LINE);
        libuvweb_add_response(uvweb, "%s", HTTP_HEAD_CHUNKED);
        libuvweb_add_response(uvweb, "%s", HTTP_HEAD_KEEPALIVE);
        //if (memmem_223(uvweb->request_head, uvweb->request_head_len, "gzip", 4) == NULL) z = NULL;
        //if (z) libuvweb_write(&uvweb->uv_tcp, HTTP_HEAD_GZIP, strlen(HTTP_HEAD_GZIP), &http_resp_buf, &http_resp_buf_size, &http_resp_buf_data_len, 0);
        if (libuvweb_add_response(uvweb, "%s", "Content-Type: text/html\r\n\r\n")) return -1;
            libuvweb_add_response_data_len_chunk(uvweb, 0, HTTP_SERVER_BODY_HEAD, strlen(HTTP_SERVER_BODY_HEAD));
    }
    //dump_stat(uvweb, dump_flag);
    libuvweb_add_response(uvweb, "%s", "0\r\n\r\n");
    libuvweb_write(uvweb);
    _uvweb_userdata_free(uvweb->data); uvweb->data = NULL;
    return 0;
}

void sig_handle_usr1(int sig) 
{
    memleak_stat(NULL, 0, NULL, 0);
}
int main()
{
    MEMLEAK_INIT;
    signal(SIGUSR1, sig_handle_usr1);
    int ret = 0;
    uv_loop_init(&uv_loop);
    int http_server_port = 1880;
    int https_mode = 0;
    const char *portmap_crt_file = "";
    const char *portmap_key_file = "";
    uv_tcp_t uv_tcp_http_server;
    libuvweb_init(&libuvweb, https_mode, portmap_crt_file, portmap_key_file, libuvweb_cb, uvweb_userdata_free, 0);
    struct sockaddr_in addr;
    uv_tcp_init(&uv_loop, &uv_tcp_http_server);
    uv_ip4_addr("0.0.0.0", http_server_port, &addr);
    ret = uv_tcp_bind(&uv_tcp_http_server, (const struct sockaddr *)&addr, 0); if (ret) LOGDIE("fail to bind to 0.0.0.0:%u\n", http_server_port);
    LOGINF("listen on 0.0.0.0:%u ok\n", http_server_port);
    uv_tcp_http_server.data = &libuvweb;
    ret = uv_listen((uv_stream_t *)&uv_tcp_http_server, 1000, on_http_new_connection); if (ret) LOGDIE("setup http_server error [%s]\n", uv_strerror(ret));
    ret = uv_run(&uv_loop, UV_RUN_DEFAULT); LOGDIE("uv_run return %d\n", ret);
    return ret;
}
